/**
 * 
 */
package com.I9lou.se.lurker.search.support;

import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.I9lou.se.lurker.search.IndexReaderFactory;
import com.I9lou.se.lurker.search.IndexSearcherFactory;

/**
 * 双Searcher工厂，每个搜索应用都将维持最多两个Searcher实例的IndexReader引用， 当通过{@link #get(int)}
 * 获取第三个所对应的搜索应用Searcher实例时， 将会关闭第一个Searcher实例的IndexReader引用，关闭后，
 * 会导致外部持有第一个Searcher实例的对象无法进行查询操作。
 * 
 * 注意：由于工厂在获取同一个搜索应用的第三个Searcher实例时，会自动关闭其引用的IndexReader。
 *      所以当通过{@link #get(int)}获取的Searcher实例不再需要使用，不需要调用#close方法进行关闭操作。
 * 
 * 注意：当前工厂实现很多方法都进行了同步，并不适合在并发环境下重复调用{@link #get(int)}， 这会带来严重的性能问题。
 * 
 * @author chenwei
 * @date 2011-03-09
 * @ThreadSafe
 */
public class DoubleIndexSearcherFactory implements IndexSearcherFactory {
	
	/**
	 * 日志输出
	 */
	private static Logger LOG = LoggerFactory.getLogger(DoubleIndexSearcherFactory.class);
	
	/**
	 * 工厂是否已执行销毁操作
	 */
	private volatile boolean destroyed = false;

	/**
	 * 上一次被引用的IndexReader缓存池
	 * Key->搜索应用ID
	 * Value->该搜索应用对应的IndexReader实例
	 */
	private HashMap<Integer, IndexReader> lastReaderPool = new HashMap<Integer, IndexReader>();

	/**
	 * 当前正在被引用的IndexReader缓存池
	 * Key->搜索应用ID
	 * Value->该搜索应用对应的IndexReader实例
	 */
	private HashMap<Integer, IndexReader> currentReaderPool = new HashMap<Integer, IndexReader>();
	
	/**
	 * Searcher多线程运行框架
	 */
	private ExecutorService pool;
	
	/**
	 * 运行Searcher的线程数
	 */
	private int poolSize = 4;
	
	/**
	 * IndexReader实例工厂
	 */
	private IndexReaderFactory indexReaderFactory = null;
	
	/**
	 * 设置运行Searcher的线程数
	 * @param poolSize
	 */
	public void setPoolSize(int poolSize) {
		this.poolSize = poolSize;
	}

	/**
	 * 设置IndexReader实例工厂
	 * 
	 * @param indexReaderFactory
	 */
	public void setIndexReaderFactory(IndexReaderFactory indexReaderFactory) {
		this.indexReaderFactory = indexReaderFactory;
	}

	@Override
	public synchronized IndexSearcher create(int appId, String[] indexDirNames) {
		if( destroyed ) {
			LOG.warn("Current factory destroyed already!");
			return null;
		}
		long startTime = System.currentTimeMillis();
		IndexReader currentReader = currentReaderPool.get(appId);
		if (currentReader == null) { // 如果该应用的Searcher是第一次被创建
			currentReader = indexReaderFactory.create(indexDirNames);
			currentReaderPool.put(appId, currentReader);
			IndexSearcher searcher = new IndexSearcher(currentReader, pool);
			LOG.info("Create Searcher[{}] success. cost: {}ms", appId, 
					 System.currentTimeMillis() - startTime);
			return searcher;
		} else {
			IndexReader newReader = null;
			try {
				newReader = currentReader.reopen();
			} catch (Exception e) {// 如果重新打开IndexReader失败，则尝试创建新的Reader
				LOG.error("Reopen IndexReader[" + appId
						  + "] failed! Try to recreate!", e);
				newReader = indexReaderFactory.create(indexDirNames);
			}
			if (newReader != currentReader) { // 如果对应的IndexReader重新打开后返回的新的实例，则进行更新操作
				IndexReader expiredReader = lastReaderPool.remove(appId); // 移除过期的IndexReader并关闭
				try {
					long closeStartTime = System.currentTimeMillis();
					if (expiredReader != null)
						expiredReader.close();
					LOG.info("Close Expired IndexReader[{}] success. cost: {}ms",
							 appId, System.currentTimeMillis() - closeStartTime);
				} catch (Exception e) {
					LOG.error("Close Expired IndexReader[" + appId
							  + "] failed!", e);
				}
				currentReaderPool.put(appId, newReader); // 将新打开的IndexReader放入当前被引用的池中
				lastReaderPool.put(appId, currentReader);// 将原来的IndexReader放入上一次被引用的池中
				IndexSearcher searcher = new IndexSearcher(newReader, pool);
				LOG.info("Recreate Searcher[{}] success. cost: {}ms", appId,
						 System.currentTimeMillis() - startTime);
				return searcher;
			} else { // 如果跟上次获取时索引信息没有发生变更，则使用原来的IndexReader对象创建Searcher后返回
				LOG.info("Ignore reopen IndexReader[{}].It remains unchange. cost: {}ms",
						 appId, System.currentTimeMillis() - startTime);
				return new IndexSearcher(currentReader, pool);
			}
		}
	}
	
	/**
	 * 当前工厂初始化函数，在服务启动时应调用此方法进行初始化操作。
	 */
	public synchronized void init() {
		long startTime = System.currentTimeMillis();
		pool = Executors.newFixedThreadPool(poolSize);
		LOG.info("DoubleSearcherFactory init finished. poolSize: {}, cost: {}ms", 
				 poolSize, System.currentTimeMillis() - startTime);
	}

	/**
	 * 当前工厂销毁函数，通常在服务关闭时调用此方法进行资源释放。
	 */
	public synchronized void destroy() {
		destroyed = true; //设置工厂销毁标志
		long startTime = System.currentTimeMillis();
		destroyPool(lastReaderPool);
		destroyPool(currentReaderPool);
		pool.shutdown();
		LOG.info("Destroy DoubleSearcherFactory over! cost: {}ms", 
				 System.currentTimeMillis() - startTime);
	}

	/**
	 * 销毁指定缓存池中的所有IndexReader实例
	 * 
	 * @param readerPool
	 */
	private synchronized void destroyPool(HashMap<Integer, IndexReader> readerPool) {
		for (Integer key : readerPool.keySet()) {
			try {
				long startTime = System.currentTimeMillis();
				lastReaderPool.get(key).close();
				LOG.info("***Close IndexReader[{}] success. cost: {}ms", key,
						 System.currentTimeMillis() - startTime);
			} catch (Exception e) {
				LOG.error("Unrecoverable error! Can't close IndexReader: " + key, e);
			}
		}
	}

}
